/* Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gruposp2p.proyectokarma.controller

import java.util.Set;
import groovy.text.SimpleTemplateEngine
import org.gruposp2p.proyectokarma.domain.Peer;
import org.gruposp2p.proyectokarma.domain.PeerRole;
import org.gruposp2p.proyectokarma.domain.Role;
import org.gruposp2p.proyectokarma.domain.RegistrationCode;
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
import org.codehaus.groovy.grails.plugins.springsecurity.NullSaltSource
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

/**
 * Based on 'RegisterController' of Grails Spring Security UI plugin
 * @author <a href='mailto:burt@burtbeckwith.com'>Burt Beckwith</a>
 */
class RegisterController {

	def springSecurityService
	def mailSenderService
	def saltSource
	def peerService

	def index = { }

	def register = { 
		def email = request.JSON.email
		if (email) {
			if (Peer.findByEmail(email)) {
				response.status = 200  
				render message(code: 'proyectokarma.register.emailRepeated', args:[email])
				return
			}
		}
		String salt = saltSource instanceof NullSaltSource ? null : email
		String password = springSecurityService.encodePassword(request.JSON.password, salt)
		def newPeer = null
		if (springSecurityService.isLoggedIn()) {
			def userDetails = springSecurityService.principal
			def peer = Peer.get(userDetails.id)
			if (peer) {
				Set<Role> peerRoles = peer.getAuthorities()
				if (peerRoles.authority.asList().contains("ROLE_ADMIN")) {
					newPeer = new Peer(username: request.JSON.email, name: request.JSON.name, email: request.JSON.email,
						password: password, accountLocked: request.JSON.accountLocked, enabled: request.JSON.enabled,
						passwordExpired:request.JSON.passwordExpired, accountExpired: request.JSON.accountExpired)
					newPeer.save()
					response.status = 200  //OK
					render message(code: 'proyectokarma.register.peerSignUp', args:[newPeer.name, newPeer.id])
					return;
				}
			}
		}
		newPeer = new Peer(username: request.JSON.email, name: request.JSON.name, email: request.JSON.email,
				password: password, accountLocked: true, enabled: true)
		def result = newPeer.save()
		if (result) {
			def registrationCode = new RegistrationCode(username: result.username).save()
			String url = generateLink('verifyRegistration', [t: registrationCode.token])
			mailSenderService.sendMail(newPeer, url, message(code: 'proyectokarma.register.peerSignUpEmailSubject'), "/mail/register");
			response.status = 200  //OK
			render message(code: 'proyectokarma.register.newPeerMsg', args:[newPeer.email])
			return;
		} else {
 			response.status = 400  //Bad Request
			render message(code: 'proyectokarma.register.newPeerErrorMsg')
			return;
 		}	
		
	}

	def verifyRegistration = {
		String token = params.t
		def registrationCode = token ? RegistrationCode.findByToken(token) : null
		if (!registrationCode) {
			response.status = 404
			render message(code: 'proyectokarma.register.badRegisterCode')
			return
		}
		def user = Peer.findByUsername(registrationCode.username)
		if (!user) {
			response.status = 404
			render message(code: 'proyectokarma.register.userNotFound')
			return
		}
		user = peerService.verifyRegistration(registrationCode, user)
		springSecurityService.reauthenticate user.username
		redirect uri: grailsApplication.config.register.postRegisterUrl
	}

	def forgotPassword = {
		String email = request.JSON.email
		if (!email) {
			response.status = 400  //Bad Request
			render message(code: 'proyectokarma.register.emailMissing')
			return
		}
		def peer = Peer.findByEmail(email)
		if (!peer) {
			response.status = 404
			render message(code: 'proyectokarma.register.emailNotFound', args:[email])
			return
		}
		def registrationCode = new RegistrationCode(username: peer.email).save()
		String url = generateLink('resetPassword', [t: registrationCode.token])
		mailSenderService.sendMail(peer, url, message(code: 'proyectokarma.register.newPasswordEmailSubject'), "/mail/forgotPassword");
		response.status = 200  //OK
		render message(code: 'proyectokarma.register.newPasswordMsg', args:[peer.email])
		return;
	}

	def resetPassword = { 
		String token = params.t		
		def registrationCode = token ? RegistrationCode.findByToken(token) : null
		if (!registrationCode) {
			response.status = 404
			render message(code: 'proyectokarma.register.badRegisterCode')
			return
		}
		if (request.get) {
			return [token:token]
		}
		if (request.method.equals('PUT')) {
			log.debug "password: " + request.JSON.password
		}
		String salt = saltSource instanceof NullSaltSource ? null : registrationCode.username
		def peer
		RegistrationCode.withTransaction { status ->
			peer = Peer.findByUsername(registrationCode.username)
			peer.password = springSecurityService.encodePassword(request.JSON.password, salt)
			peer.save()
			registrationCode.delete()
		}
		springSecurityService.reauthenticate peer.username
		response.status = 200  //OK
		render grailsApplication.config.register.postResetPassword + '?peer=' + peer.email
 	}

	protected String generateLink(String action, linkParams) {
		createLink(base: "$request.scheme://$request.serverName:$request.serverPort$request.contextPath",
				controller: 'register', action: action,
				params: linkParams)

	}


	static final passwordValidator = { String password, command ->
		if (command.username && command.username.equals(password)) {
			return 'command.password.error.username'
		}

		if (password && password.length() >= 8 && password.length() <= 64 &&
				(!password.matches('^.*\\p{Alpha}.*$') ||
				!password.matches('^.*\\p{Digit}.*$') ||
				!password.matches('^.*[!@#$%^&].*$'))) {
			return 'command.password.error.strength'
		}
	}

	static final password2Validator = { value, command ->
		if (command.password != command.password2) {
			return 'command.password2.error.mismatch'
		}
	}
}